package com.ubxtech.web.handler;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.ubxtech.core.context.Result;
import com.ubxtech.core.exception.ErrorCodeCore;
import com.ubxtech.core.exception.RRException;
import com.ubxtech.web.utils.LocaleUtil;
import com.ubxtech.web.utils.WebUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.nio.charset.Charset;
import java.util.List;


/**
 * 
 * 功能说明：异常处理器
 * 
 * RRExceptionHandler.java
 * 
 * Original Author: steven.liang,2017年9月9日上午9:42:47
 * 
 * Copyright (C)2012-2017 深圳优必选科技 All rights reserved.
 */
@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
@Slf4j
public class RRExceptionHandler {


	/**
	 * 参数校验,校验模式可以改为只校验一个错误就返回,测试是get请求是BindException,post是MethodArgumentNotValidException
	 * https://www.cnblogs.com/cjsblog/p/8946768.html
	 * @author Victor.Xiao
	 * @date 2020-05-07 10:28
	 * @param ex ex
	 * @return com.ubtechinc.model.Result<java.lang.Void>
	 */
	@ExceptionHandler(BindException.class)
	public Result<Void> resolveBindException(BindException ex){
		log.error("resolveBindException",ex);
		Result<Void> result = new Result<>(ErrorCodeCore.INVALID_PARAM_FILED);
		createResult(result);
		List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
		if(objectErrors.size()>0) {
			StringBuilder msgBuilder = new StringBuilder();
			for (ObjectError objectError : objectErrors) {
				msgBuilder.append(objectError.getDefaultMessage()).append(",");
			}
			String errorMessage = msgBuilder.toString();
			if (errorMessage.length() > 1) {
				errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
			}
			result.setMsg(errorMessage);
			//重新替换国际化信息
			LocaleUtil.replaceI18n(result);
			return result;
		}
		result.setMsg(ex.getMessage());
		return result;
	}



	/**
	 * 参数校验,校验模式可以改为只校验一个错误就返回,测试是get请求是BindException,post是MethodArgumentNotValidException
	 * https://www.cnblogs.com/cjsblog/p/8946768.html
	 * @author Victor.Xiao
	 * @date 2020-05-07 10:28
	 * @param ex ex
	 * @return com.ubtechinc.model.Result<java.lang.Void>
	*/
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public Result<Void> resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){
		log.error("resolveMethodArgumentNotValidException",ex);
		Result<Void> result = new Result<>(ErrorCodeCore.INVALID_PARAM_FILED);
		createResult(result);
		List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
		if(objectErrors.size()>0) {
			StringBuilder msgBuilder = new StringBuilder();
			for (ObjectError objectError : objectErrors) {
				msgBuilder.append(objectError.getDefaultMessage()).append(",");
			}
			String errorMessage = msgBuilder.toString();
			if (errorMessage.length() > 1) {
				errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
			}
			result.setMsg(errorMessage);
			//重新替换国际化信息
			LocaleUtil.replaceI18n(result);
			return result;
		}
		result.setMsg(ex.getMessage());
		return result;
	}


	/**
	 * 自定义异常
	 */
	@ExceptionHandler(RRException.class)
	public Result<Void> handleRRException(RRException e) {
		log.error("handleRRException",e);
		Result result = new Result<>(e.getCode(),e.getMsg());
		if (e.getData() != null) {
			result.setData(e.getData());
		}
		createResult(result);
		return result;
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public Result<Void> handleDuplicateKeyException(DuplicateKeyException e) {
		log.error("handleDuplicateKeyException",e);
		Result<Void> result = new Result<>(ErrorCodeCore.DUPLICATE_KEY);
		createResult(result);
		return result;
	}

	@ExceptionHandler(HttpMessageNotReadableException.class)
	public Result<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
		log.error("HttpMessageNotReadableException",e);
		Result<Void> result = new Result<>(ErrorCodeCore.INVALID_PARAM_FILED);
		createResult(result);
		return result;
	}

	@ExceptionHandler(DataIntegrityViolationException.class)
	public Result<String> handDataIntegrityViolationException(Exception e) {
		log.error("handDataIntegrityViolationException", e);
		Result<String> result = new Result<>(ErrorCodeCore.UPDATE_SQL_ERROR);

		if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("constraint")) {
			result.errorMsg(ErrorCodeCore.UPDATE_SQL_CONSTRAINT_ERROR);
		}
		createResult(result);
		return result;

	}


	@ExceptionHandler(Exception.class)
	public Result<String> handleException(Exception e) {
		log.error("handleException", e);
		Result<String> result = new Result<>(ErrorCodeCore.INTERNAL_SERVER_ERR);
		if (StrUtil.isNotBlank(e.getMessage())) {
			result.setMsg(e.getMessage());
			if (result.getMsg().contains("java.sql")) {
				result.errorMsg(ErrorCodeCore.DB_QUERY_ERROR);
			}
		}
		createResult(result);
		return result;

	}

	/**
	 * 发生错误时生成通用返回结果 ,requestBody是在aspect设置
	 * @author Victor.Xiao
	 * @date 2020-01-19 16:05
	 * @param result result
	*/
	public void createResult(Result result){
		try{
			// 接收到请求，记录请求内容
			ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
			HttpServletRequest request = attributes.getRequest();
			HttpServletResponse response = attributes.getResponse();
			String method = request.getMethod();
			if ("POST".equals(method)) {
				Object requestBodyObj = request.getAttribute("requestBody");
				if (request instanceof ContentCachingRequestWrapper) {
					ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
					String bodyStr = new String(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding()));
					requestBodyObj = bodyStr;
					if (StrUtil.isNotBlank(bodyStr) && bodyStr.contains("{")) {
						JSONObject bodyJson = JSONObject.parseObject(bodyStr);
						requestBodyObj = bodyJson.toJSONString();

					}
				}
				log.error("api请求地址,错误请求:url:[{}],request:[{}],response:[{}]", request.getRequestURL(), requestBodyObj, result);

			} else if ("GET".equalsIgnoreCase(method)) {
				Object requestObj = request.getParameterMap();
				log.error("api请求地址,错误请求:url:[{}],request:{},response:[{}]", request.getRequestURL(), JSONObject.toJSONString(requestObj), result);
			}
			if (result.getCode() != null && result.getCode() != 200) {
				if (response != null) {
					response.setStatus(WebUtil.resolveCode(result.getCode()).value());
				}
			}
			LocaleUtil.replaceI18n(result);
		}catch (Exception e){
			log.error("createResult error",e);
		}
	}

}